package com.ft.config;

import com.ft.model.enums.OrderStatusChangeEventEnum;
import com.ft.model.enums.OrderStatusEnum;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;

import java.util.EnumSet;

/**
 * 状态机的 状态和事件都采用String类型表示
 * 原生提供的PersistStateMachineHandler(持久化状态机处理器)中的状态和事件都采用String类型表示
 *
 * 这里将他们都改成枚举类型，自定义PersistEnumStateMachineHandler
 *
 * TODO 示例20-2和20-3存在问题：采用单例模式状态机，无法处理并发请求。
 * TODO  而且也不安全，处理时是先停止状态机，再重新开启（一旦之前未执行完，而第二个请求过来，会停止之前的，就会出错）执行状态转换
 * @author: zhangjianghua
 * @Date: 2018-10-09 17:02
 */
@Configuration
@EnableStateMachine
public class OrderStateMachineConfiguration extends EnumStateMachineConfigurerAdapter<OrderStatusEnum, OrderStatusChangeEventEnum> {

    /**
     * 配置状态
     * @param states
     * @throws Exception
     */
    @Override
    public void configure(StateMachineStateConfigurer<OrderStatusEnum, OrderStatusChangeEventEnum> states) throws Exception {
        states
                .withStates()
                .initial(OrderStatusEnum.WAIT_PAYMENT)
                .states(EnumSet.allOf(OrderStatusEnum.class));
    }
    /**
     * 配置状态转换事件关系
     * @param transitions
     * @throws Exception
     */
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStatusEnum, OrderStatusChangeEventEnum> transitions) throws Exception {
        transitions
                .withExternal().source(OrderStatusEnum.WAIT_PAYMENT).target(OrderStatusEnum.WAIT_DELIVER).event(OrderStatusChangeEventEnum.PAYED)
                .and()
                .withExternal().source(OrderStatusEnum.WAIT_DELIVER).target(OrderStatusEnum.WAIT_RECEIVE).event(OrderStatusChangeEventEnum.DELIVERY)
                .and()
                .withExternal().source(OrderStatusEnum.WAIT_RECEIVE).target(OrderStatusEnum.FINISH).event(OrderStatusChangeEventEnum.RECEIVED);

    }
}
